--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
tests/backend/test_integrity_extensive.py 56d219df611efdff155d9bf5a355970d8569c098 (56d219df) Text, 7.06 KB
Tff7b72import T7ee787shutil
Tff7b72import T7ee787tempfile
Tff7b72import T7ee787unittest
Tff7b72import T7ee787os
Tff7b72import T7ee787sqlite3
Tff7b72import T7ee787json
Tff7b72from T7ee787pathlib Tff7b72import Te6edf3Path
Tff7b72from T7ee787hypothesis Tff7b72import Te6edf3givenTb4b4b4, Te6edf3strategies Tff7b72as Te6edf3stTb4b4b4, Te6edf3settingsTb4b4b4, Te6edf3HealthCheck
Tff7b72from T7ee787meshchatxT7ee787.T7ee787srcT7ee787.T7ee787backendT7ee787.T7ee787integrity_manager Tff7b72import Te6edf3IntegrityManager
Tff7b72class T56d364TestIntegrityManagerExtensiveTb4b4b4(Te6edf3unittestTff7b72.Td2a8ffTestCaseTb4b4b4)Tb4b4b4:
Tff7b72def Td2a8ffsetUpTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
Tff7b72selfTff7b72.Td2a8fftest_dir Tff7b72= Te6edf3PathTb4b4b4(Te6edf3tempfileTff7b72.Td2a8ffmkdtempTb4b4b4(Tb4b4b4)Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffdb_path Tff7b72= Tff7b72selfTff7b72.Td2a8fftest_dir Tff7b72/ Ta5d6ff"Ta5d6ffdatabase.dbTa5d6ff"
Tff7b72selfTff7b72.Td2a8ffstorage_dir Tff7b72= Tff7b72selfTff7b72.Td2a8fftest_dir Tff7b72/ Ta5d6ff"Ta5d6ffstorageTa5d6ff"
Tff7b72selfTff7b72.Td2a8ffstorage_dirTff7b72.Td2a8ffmkdirTb4b4b4(Tb4b4b4)
T8b949e# Create a valid SQLite database
Te6edf3conn Tff7b72= Te6edf3sqlite3Tff7b72.Td2a8ffconnectTb4b4b4(Tff7b72selfTff7b72.Td2a8ffdb_pathTb4b4b4)
Te6edf3connTff7b72.Td2a8ffexecuteTb4b4b4(Ta5d6ff"Ta5d6ffCREATE TABLE data (id INTEGER PRIMARY KEY, val TEXT)Ta5d6ff"Tb4b4b4)
Te6edf3connTff7b72.Td2a8ffexecuteTb4b4b4(Ta5d6ff"Ta5d6ffINSERT INTO data (val) VALUES (Ta5d6ff'Ta5d6ffinitialTa5d6ff'Ta5d6ff)Ta5d6ff"Tb4b4b4)
Te6edf3connTff7b72.Td2a8ffcommitTb4b4b4(Tb4b4b4)
Te6edf3connTff7b72.Td2a8ffcloseTb4b4b4(Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffmanager Tff7b72= Te6edf3IntegrityManagerTb4b4b4(Tff7b72selfTff7b72.Td2a8fftest_dirTb4b4b4, Tff7b72selfTff7b72.Td2a8ffdb_pathTb4b4b4)
Tff7b72def Td2a8fftearDownTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
Te6edf3shutilTff7b72.Td2a8ffrmtreeTb4b4b4(Tff7b72selfTff7b72.Td2a8fftest_dirTb4b4b4)
Tff7b72def Td2a8fftest_entropy_mathematical_boundsTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
T8b949e"""Verify entropy stays within [0, 8] for any byte sequence."""
T8b949e# Test empty
Te6edf3empty_file Tff7b72= Tff7b72selfTff7b72.Td2a8fftest_dir Tff7b72/ Ta5d6ff"Ta5d6ffemptyTa5d6ff"
Te6edf3empty_fileTff7b72.Td2a8fftouchTb4b4b4(Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertEqualTb4b4b4(Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ff_calculate_entropyTb4b4b4(Te6edf3empty_fileTb4b4b4)Tb4b4b4, T79c0ff0Tb4b4b4)
T8b949e# Test single byte repeated (minimum entropy)
Te6edf3zero_entropy_file Tff7b72= Tff7b72selfTff7b72.Td2a8fftest_dir Tff7b72/ Ta5d6ff"Ta5d6ffzero_entropyTa5d6ff"
Tff7b72with Tffa657openTb4b4b4(Te6edf3zero_entropy_fileTb4b4b4, Ta5d6ff"Ta5d6ffwbTa5d6ff"Tb4b4b4) Tff7b72as Te6edf3fTb4b4b4:
Te6edf3fTff7b72.Td2a8ffwriteTb4b4b4(Ta5d6ffbTa5d6ff"Ta5d6ffAAAAAAAATa5d6ff" Tff7b72* T79c0ff100Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertEqualTb4b4b4(Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ff_calculate_entropyTb4b4b4(Te6edf3zero_entropy_fileTb4b4b4)Tb4b4b4, T79c0ff0Tb4b4b4)
T8b949e# Test all 256 bytes (maximum entropy)
Te6edf3max_entropy_file Tff7b72= Tff7b72selfTff7b72.Td2a8fftest_dir Tff7b72/ Ta5d6ff"Ta5d6ffmax_entropyTa5d6ff"
Tff7b72with Tffa657openTb4b4b4(Te6edf3max_entropy_fileTb4b4b4, Ta5d6ff"Ta5d6ffwbTa5d6ff"Tb4b4b4) Tff7b72as Te6edf3fTb4b4b4:
Te6edf3fTff7b72.Td2a8ffwriteTb4b4b4(Tffa657bytesTb4b4b4(Tffa657rangeTb4b4b4(T79c0ff256Tb4b4b4)Tb4b4b4)Tb4b4b4)
T8b949e# log2(256) = 8
Tff7b72selfTff7b72.Td2a8ffassertAlmostEqualTb4b4b4(
Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ff_calculate_entropyTb4b4b4(Te6edf3max_entropy_fileTb4b4b4)Tb4b4b4, T79c0ff8.0Tb4b4b4, Te6edf3placesTff7b72=T79c0ff5
Tb4b4b4)
Tf0883e@settingsTb4b4b4(Te6edf3suppress_health_checkTff7b72=Tb4b4b4[Te6edf3HealthCheckTff7b72.Td2a8fftoo_slowTb4b4b4]Tb4b4b4, Te6edf3deadlineTff7b72=Tff7b72NoneTb4b4b4)
Tf0883e@givenTb4b4b4(Te6edf3stTff7b72.Td2a8ffbinaryTb4b4b4(Te6edf3min_sizeTff7b72=T79c0ff1Tb4b4b4, Te6edf3max_sizeTff7b72=T79c0ff1024Tb4b4b4)Tb4b4b4)
Tff7b72def Td2a8fftest_entropy_propertyTb4b4b4(Tff7b72selfTb4b4b4, Te6edf3dataTb4b4b4)Tb4b4b4:
T8b949e"""Property: Entropy is always between 0 and 8 for non-empty data."""
Te6edf3temp_file Tff7b72= Tff7b72selfTff7b72.Td2a8fftest_dir Tff7b72/ Ta5d6ff"Ta5d6ffprop_testTa5d6ff"
Tff7b72with Tffa657openTb4b4b4(Te6edf3temp_fileTb4b4b4, Ta5d6ff"Ta5d6ffwbTa5d6ff"Tb4b4b4) Tff7b72as Te6edf3fTb4b4b4:
Te6edf3fTff7b72.Td2a8ffwriteTb4b4b4(Te6edf3dataTb4b4b4)
Te6edf3entropy Tff7b72= Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ff_calculate_entropyTb4b4b4(Te6edf3temp_fileTb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertGreaterEqualTb4b4b4(Te6edf3entropyTb4b4b4, T79c0ff0Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertLessEqualTb4b4b4(Te6edf3entropyTb4b4b4, T79c0ff8.000000000000002Tb4b4b4) T8b949e# Float precision
Tff7b72def Td2a8fftest_db_structural_tamper_detectionTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
T8b949e"""Simulate actual SQLite corruption that bypasses hash-only checks."""
Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffsave_manifestTb4b4b4(Tb4b4b4)
T8b949e# Corrupt the database file header or internal structure
T8b949e# Overwriting the first few bytes (SQLite header) is a guaranteed fail
Tff7b72with Tffa657openTb4b4b4(Tff7b72selfTff7b72.Td2a8ffdb_pathTb4b4b4, Ta5d6ff"Ta5d6ffr+bTa5d6ff"Tb4b4b4) Tff7b72as Te6edf3fTb4b4b4:
Te6edf3fTff7b72.Td2a8ffseekTb4b4b4(T79c0ff0Tb4b4b4)
Te6edf3fTff7b72.Td2a8ffwriteTb4b4b4(Ta5d6ffbTa5d6ff"Ta5d6ffNOTASQLITEFILETa5d6ff"Tb4b4b4)
Te6edf3is_okTb4b4b4, Te6edf3issues Tff7b72= Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffcheck_integrityTb4b4b4(Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertFalseTb4b4b4(Te6edf3is_okTb4b4b4, Ta5d6fffTa5d6ff"Ta5d6ffIntegrity should fail. Issues: Tffd700{Te6edf3issuesTffd700}Ta5d6ff"Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertTrueTb4b4b4(
Tffa657anyTb4b4b4(
Ta5d6ff"Ta5d6ffDatabase structural issueTa5d6ff" Tff7b72in Te6edf3i Tff7b72or Ta5d6ff"Ta5d6ffDatabase structural anomalyTa5d6ff" Tff7b72in Te6edf3i
Tff7b72for Te6edf3i Tff7b72in Te6edf3issues
Tb4b4b4)Tb4b4b4,
Ta5d6fffTa5d6ff"Ta5d6ffExpected structural issue in: Tffd700{Te6edf3issuesTffd700}Ta5d6ff"Tb4b4b4,
Tb4b4b4)
Tff7b72def Td2a8fftest_entropy_shift_detectionTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
T8b949e"""Test detection of content type change (e.g. replacing text with random bytes)."""
T8b949e# 1. Start with a highly structured file (low entropy)
T8b949e# Use a non-critical filename to trigger the entropy check branch
Te6edf3data_file Tff7b72= Tff7b72selfTff7b72.Td2a8fftest_dir Tff7b72/ Ta5d6ff"Ta5d6ffuser_data.binTa5d6ff"
Tff7b72with Tffa657openTb4b4b4(Te6edf3data_fileTb4b4b4, Ta5d6ff"Ta5d6ffwbTa5d6ff"Tb4b4b4) Tff7b72as Te6edf3fTb4b4b4:
Te6edf3fTff7b72.Td2a8ffwriteTb4b4b4(Ta5d6ffbTa5d6ff"Ta5d6ffATa5d6ff" Tff7b72* T79c0ff5000Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffsave_manifestTb4b4b4(Tb4b4b4)
T8b949e# 2. Replace with high-entropy data (random bytes)
Tff7b72with Tffa657openTb4b4b4(Te6edf3data_fileTb4b4b4, Ta5d6ff"Ta5d6ffwbTa5d6ff"Tb4b4b4) Tff7b72as Te6edf3fTb4b4b4:
Te6edf3fTff7b72.Td2a8ffwriteTb4b4b4(Te6edf3osTff7b72.Td2a8ffurandomTb4b4b4(T79c0ff5000Tb4b4b4)Tb4b4b4)
Te6edf3is_okTb4b4b4, Te6edf3issues Tff7b72= Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffcheck_integrityTb4b4b4(Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertFalseTb4b4b4(Te6edf3is_okTb4b4b4, Ta5d6fffTa5d6ff"Ta5d6ffIntegrity should fail. Issues: Tffd700{Te6edf3issuesTffd700}Ta5d6ff"Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertTrueTb4b4b4(
Tffa657anyTb4b4b4(Ta5d6ff"Ta5d6ffNon-linear content shiftTa5d6ff" Tff7b72in Te6edf3i Tff7b72or Ta5d6ff"Ta5d6ffEntropy ΔTa5d6ff" Tff7b72in Te6edf3i Tff7b72for Te6edf3i Tff7b72in Te6edf3issuesTb4b4b4)Tb4b4b4,
Ta5d6fffTa5d6ff"Ta5d6ffExpected entropy shift in: Tffd700{Te6edf3issuesTffd700}Ta5d6ff"Tb4b4b4,
Tb4b4b4)
Tff7b72def Td2a8fftest_ignore_patterns_extensiveTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
T8b949e"""Verify all volatile LXMF/RNS patterns are correctly filtered."""
Te6edf3volatile_files Tff7b72= Tb4b4b4[
Ta5d6ff"Ta5d6fflxmf_router/lxmf/outbound_stamp_costsTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6fflxmf_router/storage/some_volatile_fileTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6fflxmf_router/announces/ann_dataTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6fflxmf_router/tmp/uploadingTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffdatabase.db-walTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffdatabase.db-shmTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffsomething.tmpTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ff.DS_StoreTa5d6ff"Tb4b4b4,
Tb4b4b4]
Tff7b72for Te6edf3v Tff7b72in Te6edf3volatile_filesTb4b4b4:
Te6edf3rel_path Tff7b72= Te6edf3PathTb4b4b4(Te6edf3vTb4b4b4)
Te6edf3full_path Tff7b72= Tff7b72selfTff7b72.Td2a8fftest_dir Tff7b72/ Te6edf3rel_path
Te6edf3full_pathTff7b72.Td2a8ffparentTff7b72.Td2a8ffmkdirTb4b4b4(Te6edf3parentsTff7b72=Tff7b72TrueTb4b4b4, Te6edf3exist_okTff7b72=Tff7b72TrueTb4b4b4)
Te6edf3full_pathTff7b72.Td2a8fftouchTb4b4b4(Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertTrueTb4b4b4(
Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ff_should_ignoreTb4b4b4(Tffa657strTb4b4b4(Te6edf3rel_pathTb4b4b4)Tb4b4b4)Tb4b4b4, Ta5d6fffTa5d6ff"Ta5d6ffFailed to ignore Tffd700{Te6edf3vTffd700}Ta5d6ff"
Tb4b4b4)
Tff7b72def Td2a8fftest_critical_file_protectionTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
T8b949e"""Ensure identity and config changes are always treated as critical."""
Te6edf3id_file Tff7b72= Tff7b72selfTff7b72.Td2a8fftest_dir Tff7b72/ Ta5d6ff"Ta5d6ffidentityTa5d6ff"
Te6edf3id_fileTff7b72.Td2a8ffwrite_textTb4b4b4(Ta5d6ff"Ta5d6ffsecure_keyTa5d6ff"Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffsave_manifestTb4b4b4(Tb4b4b4)
T8b949e# Minor modification (stays low entropy)
Te6edf3id_fileTff7b72.Td2a8ffwrite_textTb4b4b4(Ta5d6ff"Ta5d6ffsecure_kezTa5d6ff"Tb4b4b4)
Te6edf3is_okTb4b4b4, Te6edf3issues Tff7b72= Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffcheck_integrityTb4b4b4(Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertFalseTb4b4b4(Te6edf3is_okTb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertTrueTb4b4b4(Tffa657anyTb4b4b4(Ta5d6ff"Ta5d6ffCritical security componentTa5d6ff" Tff7b72in Te6edf3i Tff7b72for Te6edf3i Tff7b72in Te6edf3issuesTb4b4b4)Tb4b4b4)
Tff7b72def Td2a8fftest_missing_file_detectionTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
T8b949e"""Verify missing files are detected even if not critical."""
Te6edf3misc_file Tff7b72= Tff7b72selfTff7b72.Td2a8fftest_dir Tff7b72/ Ta5d6ff"Ta5d6ffmisc.txtTa5d6ff"
Te6edf3misc_fileTff7b72.Td2a8ffwrite_textTb4b4b4(Ta5d6ff"Ta5d6ffdataTa5d6ff"Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffsave_manifestTb4b4b4(Tb4b4b4)
Te6edf3misc_fileTff7b72.Td2a8ffunlinkTb4b4b4(Tb4b4b4)
Te6edf3is_okTb4b4b4, Te6edf3issues Tff7b72= Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffcheck_integrityTb4b4b4(Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertFalseTb4b4b4(Te6edf3is_okTb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertTrueTb4b4b4(Tffa657anyTb4b4b4(Ta5d6ff"Ta5d6ffFile missing: misc.txtTa5d6ff" Tff7b72in Te6edf3i Tff7b72for Te6edf3i Tff7b72in Te6edf3issuesTb4b4b4)Tb4b4b4)
Tff7b72def Td2a8fftest_manifest_versioningTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
T8b949e"""Verify the manifest includes the new version and metadata fields."""
Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffsave_manifestTb4b4b4(Tb4b4b4)
Tff7b72with Tffa657openTb4b4b4(Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffmanifest_pathTb4b4b4) Tff7b72as Te6edf3fTb4b4b4:
Te6edf3manifest Tff7b72= Te6edf3jsonTff7b72.Td2a8ffloadTb4b4b4(Te6edf3fTb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertEqualTb4b4b4(Te6edf3manifestTb4b4b4[Ta5d6ff"Ta5d6ffversionTa5d6ff"Tb4b4b4]Tb4b4b4, T79c0ff2Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertInTb4b4b4(Ta5d6ff"Ta5d6ffmetadataTa5d6ff"Tb4b4b4, Te6edf3manifestTb4b4b4)
T8b949e# Check if database metadata exists
Te6edf3db_rel Tff7b72= Tffa657strTb4b4b4(Tff7b72selfTff7b72.Td2a8ffdb_pathTff7b72.Td2a8ffrelative_toTb4b4b4(Tff7b72selfTff7b72.Td2a8fftest_dirTb4b4b4)Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertInTb4b4b4(Te6edf3db_relTb4b4b4, Te6edf3manifestTb4b4b4[Ta5d6ff"Ta5d6ffmetadataTa5d6ff"Tb4b4b4]Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertInTb4b4b4(Ta5d6ff"Ta5d6ffentropyTa5d6ff"Tb4b4b4, Te6edf3manifestTb4b4b4[Ta5d6ff"Ta5d6ffmetadataTa5d6ff"Tb4b4b4]Tb4b4b4[Te6edf3db_relTb4b4b4]Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertInTb4b4b4(Ta5d6ff"Ta5d6ffsizeTa5d6ff"Tb4b4b4, Te6edf3manifestTb4b4b4[Ta5d6ff"Ta5d6ffmetadataTa5d6ff"Tb4b4b4]Tb4b4b4[Te6edf3db_relTb4b4b4]Tb4b4b4)
Tff7b72def Td2a8fftest_database_size_divergenceTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
T8b949e"""Verify size changes are caught when hash changes but entropy is similar."""
Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffsave_manifestTb4b4b4(Tb4b4b4)
T8b949e# Grow the database with similar content
Te6edf3conn Tff7b72= Te6edf3sqlite3Tff7b72.Td2a8ffconnectTb4b4b4(Tff7b72selfTff7b72.Td2a8ffdb_pathTb4b4b4)
Te6edf3connTff7b72.Td2a8ffexecuteTb4b4b4(Ta5d6ff"Ta5d6ffINSERT INTO data (val) VALUES (?)Ta5d6ff"Tb4b4b4, Tb4b4b4(Ta5d6ff"Ta5d6ffmore contentTa5d6ff" Tff7b72* T79c0ff100Tb4b4b4,Tb4b4b4)Tb4b4b4)
Te6edf3connTff7b72.Td2a8ffcommitTb4b4b4(Tb4b4b4)
Te6edf3connTff7b72.Td2a8ffcloseTb4b4b4(Tb4b4b4)
Te6edf3is_okTb4b4b4, Te6edf3issues Tff7b72= Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffcheck_integrityTb4b4b4(Tb4b4b4)
T8b949e# Even if entropy shift is low, hash and size changed
Tff7b72if Tff7b72not Te6edf3is_okTb4b4b4:
Tff7b72selfTff7b72.Td2a8ffassertTrueTb4b4b4(Tffa657anyTb4b4b4(Ta5d6ff"Ta5d6ffDatabaseTa5d6ff" Tff7b72in Te6edf3i Tff7b72for Te6edf3i Tff7b72in Te6edf3issuesTb4b4b4)Tb4b4b4)
Tff7b72if Tff7b72__name__ Tff7b72== Ta5d6ff"Ta5d6ff__main__Ta5d6ff"Tb4b4b4:
Te6edf3unittestTff7b72.Td2a8ffmainTb4b4b4(Tb4b4b4)
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────